package com.aim.azure;

import com.aim.analysis.records.*;
import com.aim.util.filters.Filter;
import com.aim.util.filters.FilterUtil;
import com.microsoft.windowsazure.services.core.storage.*;
import com.microsoft.windowsazure.services.table.client.CloudTableClient;
import com.microsoft.windowsazure.services.table.client.TableQuery;
import com.microsoft.windowsazure.services.table.client.TableServiceEntity;

import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.util.*;
import java.util.logging.Logger;

public class AzureConnection
{
    static Logger logger = Logger.getLogger(AzureConnection.class.getCanonicalName());


    // Substitute your values for the storage account name and key.
    public static final String storageAccountName = "mggame";
    public static final String storageAccountKey = "9CTpj7VljH5GzLAK0jlypwjHCUg65hAj45lniaDyFzi3p55TGIY5fBUuftEQGbOKAmaxn8d26DTSiDdTR8eeeA==";

    // Create the storage connection string based on the account name and key.
    public static final String storageConnectionString =
            "DefaultEndpointsProtocol=http;" +
                    "AccountName=" + storageAccountName + ";" +
                    "AccountKey=" + storageAccountKey;

    private static final String DEMOGRAPHIC_TABLE_NAME_PREFIX = "DemographicDate";
    private static final String GAME_TABLE_NAME_PREFIX = "GameMessageDate";
    private static final String GAME_QUERY_TABLE_NAME_PREFIX = "GameQuestionnaireDate";

    public CloudStorageAccount storageAccount;
    public CloudTableClient cloudTableClient;

    private List<String> experimentDatesList = new ArrayList<String>();

    private Filter<String> assignmentIdFilter = FilterUtil.dummy(String.class);

    public void setAssignmentIdFilter(Filter<String> assignmentIdFilter) {
        this.assignmentIdFilter = assignmentIdFilter;
    }

    public void addExperimentDate(String experimentDate) {
        this.experimentDatesList.add(experimentDate);
    }

    public void connect() throws URISyntaxException, InvalidKeyException {
            storageAccount = CloudStorageAccount.parse(storageConnectionString);
            cloudTableClient = storageAccount.createCloudTableClient();
    }

    public List<AzureGameRecordEntity> getGameRecords(){
        Filter<AzureGameRecordEntity> filter = new Filter<AzureGameRecordEntity>() {
            @Override
            public boolean accept(AzureGameRecordEntity entity) {
                return assignmentIdFilter.accept(entity.getPartitionKey());
            }
        };
        List<AzureGameRecordEntity> records = getRecords(GAME_TABLE_NAME_PREFIX, AzureGameRecordEntity.class, filter);
        return records;
    }

    public List<AzureDemographicEntity> getDemographicRecords(){
        Filter<AzureDemographicEntity> filter = new Filter<AzureDemographicEntity>() {
            @Override
            public boolean accept(AzureDemographicEntity entity) {
                return assignmentIdFilter.accept(entity.getPartitionKey());
            }
        };
        return getRecords(DEMOGRAPHIC_TABLE_NAME_PREFIX, AzureDemographicEntity.class, filter);
    }

    public GameMap getOrCreateGameMap(){
        return getOrCreateGameMap(GameRecord.dummyFilter());
    }

    public GameMap getOrCreateGameMap(Filter<GameRecord> recordFilter){

        String gameTableName = GAME_TABLE_NAME_PREFIX + experimentDatesList;
        TableQuery<AzureGameRecordEntity> tableQuery = TableQuery.from(gameTableName, AzureGameRecordEntity.class);
        Iterable<AzureGameRecordEntity> execute = cloudTableClient.execute(tableQuery);


        GameMap gameMap = new GameMap(recordFilter);
        for (AzureGameRecordEntity recordEntity : execute){
            GameRecord gameRecord = GameRecord.fromAzureRecord(recordEntity);
            gameMap.addRecord(gameRecord);
            //System.out.println(gameRecord);
        }
        gameMap.postProcessing();
        return gameMap;
    }

    public Set<String> getSucceededPlayersAssignmentId(String experimentDate){
        String gameTableName = GAME_QUERY_TABLE_NAME_PREFIX + experimentDate;
        TableQuery<AzureGameQuestionnaireEntity> tableQuery = TableQuery.from(gameTableName, AzureGameQuestionnaireEntity.class);
        Iterable<AzureGameQuestionnaireEntity> execute = cloudTableClient.execute(tableQuery);

        Set<String> assignmentIds = new HashSet<String>();
        for (AzureGameQuestionnaireEntity entity : execute){
            assignmentIds.add(entity.getPartitionKey());
        }
        return assignmentIds;
    }



    public List<AzureGameQuestionnaireEntity> getGameQuestionsRecords() {
        Filter<AzureGameQuestionnaireEntity> filter = new Filter<AzureGameQuestionnaireEntity>() {
            @Override
            public boolean accept(AzureGameQuestionnaireEntity azureGameRecordEntity) {
                return assignmentIdFilter.accept(azureGameRecordEntity.getPartitionKey());
            }
        };
        return getRecords(GAME_QUERY_TABLE_NAME_PREFIX, AzureGameQuestionnaireEntity.class, filter);
    }


    private <ENTITY extends TableServiceEntity> List<ENTITY> getRecords(String tablePrefix, Class<ENTITY> clazz, Filter<ENTITY> filter){
        List<ENTITY> records = new ArrayList<ENTITY>();
        for (String experimentDate : experimentDatesList) {
            logger.info("loading " + clazz.getName() + "records for date " +experimentDate);
            String gameTableName = tablePrefix + experimentDate;
            TableQuery<ENTITY> tableQuery = TableQuery.from(gameTableName, clazz);
            Iterable<ENTITY> execute = cloudTableClient.execute(tableQuery);

            for (ENTITY entity : execute){
                if (filter.accept(entity)){
                    records.add(entity);
                }
            }
        }


        return records;
    }

    public static void main(String[] args)
    {
        try
        {
            AzureConnection azureConnection = new AzureConnection();
            azureConnection.connect();

            String experimentDate = "20140616";
            final Set<String> succeededPlayersAssignmentId = azureConnection.getSucceededPlayersAssignmentId(experimentDate);
            Filter<GameRecord> gameRecordFilter = new Filter<GameRecord>() {
                @Override
                public boolean accept(GameRecord record) {
                    return succeededPlayersAssignmentId.contains(record.gameKey.partitionKey);
                }
            };
            GameMap gameMap = azureConnection.getOrCreateGameMap(gameRecordFilter);

            List<GameValueExtractor> valueExtractors = new ArrayList<GameValueExtractor>();

            // header
            valueExtractors.add(GameValueExtractor.partitionKey);
            valueExtractors.add(GameValueExtractor.gameDescription);
            valueExtractors.add(GameValueExtractor.gameNumber);

            //mechanical - actions & costs
            valueExtractors.add(GameValueExtractor.maintenance);
            valueExtractors.add(GameValueExtractor.minor);
            valueExtractors.add(GameValueExtractor.moderate);
            valueExtractors.add(GameValueExtractor.severe);
            valueExtractors.add(GameValueExtractor.maintenanceCost);
            valueExtractors.add(GameValueExtractor.repairCost);
            valueExtractors.add(GameValueExtractor.mechanicalCost);

            //refuel - actions & costs
            valueExtractors.add(GameValueExtractor.refuelByStations);
            valueExtractors.add(GameValueExtractor.refuelByStationsCost);
            valueExtractors.add(GameValueExtractor.refuelBySpaceship);
            valueExtractors.add(GameValueExtractor.refuelBySpaceshipCost);
            valueExtractors.add(GameValueExtractor.totalRefuelCost);

            //time
            valueExtractors.add(GameValueExtractor.mechanicalTime);
            valueExtractors.add(GameValueExtractor.refuelBySpaceshipTime);
            valueExtractors.add(GameValueExtractor.freezeTime);


            //performance
            valueExtractors.add(GameValueExtractor.totalShutdown);
            valueExtractors.add(GameValueExtractor.totalCollision);
            valueExtractors.add(GameValueExtractor.totalFire);
            valueExtractors.add(GameValueExtractor.netScore);

            // score
            valueExtractors.add(GameValueExtractor.score);
            valueExtractors.add(GameValueExtractor.calculatedScore);


            Filter<Game> gameFilter = new Filter<Game>() {
                @Override
                public boolean accept(Game game) {
                    return succeededPlayersAssignmentId.contains(game.getKey().partitionKey);
                }
            };
            GameReport gameReport = new GameReport(valueExtractors);
            gameReport.printReport(gameMap.getAllGames(), gameFilter);

//            List<Game> byName = gameMap.getByName("37ZHEEHM6WN2MUELW5YX94TJKR773P");
//            Game g = byName.get(0);
//            System.out.println(Arrays.toString(g.getTimes(RecordType.fuelFill)));
//            System.out.println(Arrays.toString(g.getTimes(RecordType.maintenaneAction)));
//            System.out.println(Arrays.toString(g.getTimes(RecordType.repairAction)));
//            System.out.println(Arrays.toString(g.getRepairTimes(MC.M1)));
//            System.out.println(Arrays.toString(g.getRepairTimes(MC.M2)));
//            System.out.println(Arrays.toString(g.getRepairTimes(MC.M3)));
//            System.out.println(Arrays.toString(g.getTimes(RecordType.mechanicalConditionChanged)));
//            System.out.println();
//            System.out.println(Arrays.toString(g.getCollisionTimes()));
//            System.out.println(Arrays.toString(g.getShutdownTimes()));
//            System.out.println(Arrays.toString(g.getFireTimes()));
//            System.out.println(byName.size());


            System.out.println();

        } catch (Exception e) {
            System.out.println("Exception encountered.");
            e.printStackTrace();
        }


    }
}
